package cn.antcore.resources.core;

import cn.antcore.resources.config.RefreshConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.Banner;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.*;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 资源配置刷新
 * Created By Hong on 2018/8/7
 **/
@Component(value = "ResourcesRefresher")
public class ResourcesRefresher implements RefreshConfig {

    private static final Logger LOG = LoggerFactory.getLogger(ResourcesRefresher.class);

    private static final String[] DEFAULT_PROPERTY_SOURCES = new String[]{"commandLineArgs", "defaultProperties"};
    private ConfigurableApplicationContext context;

    public ResourcesRefresher(ConfigurableApplicationContext context) {
        this.context = context;
    }

    @Override
    public synchronized void refresh() {
        ConfigurableEnvironment oldEnvironment = this.context.getEnvironment();
        MutablePropertySources oldSource = this.context.getEnvironment().getPropertySources();
        MutablePropertySources sources = this.addConfigFilesToEnvironment(oldEnvironment);
        Iterator<PropertySource<?>> iterator = sources.iterator();
        while (iterator.hasNext()) {
            PropertySource source = iterator.next();
            if (oldSource.contains(source.getName())) {
                oldSource.replace(source.getName(), source);
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Replace " + source.getName() + " Successful.");
                }
            }
        }
    }

    /**
     * 构建一个非Web的SpringContext来读取最新的配置
     *
     * @param oldEnvironment ConfigurableEnvironment
     * @return MutablePropertySources
     */
    private MutablePropertySources addConfigFilesToEnvironment(ConfigurableEnvironment oldEnvironment) {
        ConfigurableApplicationContext capture = null;
        MutablePropertySources mutablePropertySources;
        try {
            StandardEnvironment environment = this.copyEnvironment(oldEnvironment);
            SpringApplicationBuilder builder = (new SpringApplicationBuilder(new Object[]{ResourcesRefresher.Empty.class})).bannerMode(Banner.Mode.OFF).web(false).environment(environment);
            capture = builder.run(new String[0]);
            mutablePropertySources = capture.getEnvironment().getPropertySources();
        } finally {
            for (ConfigurableApplicationContext closeable = capture; closeable != null; closeable = (ConfigurableApplicationContext) closeable.getParent()) {
                try {
                    closeable.close();
                } catch (Exception var16) {
                }
            }
        }
        return mutablePropertySources;
    }


    private StandardEnvironment copyEnvironment(ConfigurableEnvironment input) {
        StandardEnvironment environment = new StandardEnvironment();
        MutablePropertySources capturedPropertySources = environment.getPropertySources();
        String[] var4 = DEFAULT_PROPERTY_SOURCES;
        int var5 = var4.length;

        for (int var6 = 0; var6 < var5; ++var6) {
            String name = var4[var6];
            if (input.getPropertySources().contains(name)) {
                if (capturedPropertySources.contains(name)) {
                    capturedPropertySources.replace(name, input.getPropertySources().get(name));
                } else {
                    capturedPropertySources.addLast(input.getPropertySources().get(name));
                }
            }
        }

        environment.setActiveProfiles(input.getActiveProfiles());
        environment.setDefaultProfiles(input.getDefaultProfiles());
        Map<String, Object> map = new HashMap();
        map.put("spring.jmx.enabled", false);
        map.put("spring.main.sources", "");
        capturedPropertySources.addFirst(new MapPropertySource("refreshArgs", map));
        return environment;
    }

    @Configuration
    protected static class Empty {
        protected Empty() {
        }
    }
}
